import {
  CreateTreeNodeOptionFormList,
  ListTreeNode,
  CreateTreeNodeOption,
  TreeNode,
  TreeCache,
} from './interface'
import { getChildren, getLabel, isDisabled, isKey, isParentKey } from './preset'

/**
 * 通过单层数组创建树状数据
 * @param options
 * @param cache
 */
export function createTreeNodeFormArray<Raw extends Record<string, any>>(
  options: CreateTreeNodeOptionFormList<Raw>,
  cache: TreeCache<Raw>
) {
  const {
    raw,
    disabled = isDisabled,
    key = isKey,
    parentKey = isParentKey,
    label = getLabel,
    makeNomatchParentToTop = false,
  } = options
  const data: Array<ListTreeNode<Raw>> = []
  const tree: Array<ListTreeNode<Raw>> = []
  const keyMap = {}
  let node: any

  raw.forEach((item, i) => {
    data[i] = {
      _disabled: disabled(item),
      _parentKey: parentKey(item),
      _key: key(item),
      _label: label(item),
      _children: [],
      ...item,
    }
    keyMap[data[i]._key] = data[i]
  })

  data.forEach((item) => {
    node = item
    if (!node._parentKey) {
      tree.push(node)
      return
    }
    if (keyMap[node._parentKey]) {
      keyMap[node._parentKey]._children.push(node)
      return
    }
    if (makeNomatchParentToTop) {
      tree.push(node)
    }
  })
  return createTreeNode<ListTreeNode<Raw>>(
    {
      ...(options as any),
      raw: tree,
      disabled: (node) => node._disabled,
      children: (node) => node._children,
      key: (node) => node._key,
      label: (node) => node._label,
    },
    cache as any
  )
}

/**
 * 树状数据重新递归解析，为了使树状数据更好的被解析
 * @param options
 * @param cache
 */
export function createTreeNode<Raw extends Record<string, any>>(
  options: CreateTreeNodeOption<Raw>,
  cache: TreeCache<Raw>
) {
  const {
    raw,
    disabled = isDisabled,
    key = isKey,
    children = getChildren,
    label = getLabel,
    level = 1,
    parent,
    customCache = {},
    forEach,
  } = options
  const treeNodes: Array<TreeNode<Raw>> = []

  raw.forEach((rawNode, index) => {
    const treeNode: TreeNode<Raw> = {
      disabled: disabled(rawNode),
      key: key(rawNode),
      label: label(rawNode),
      raw: rawNode,
      siblings: treeNodes,
      level,
      index,
      parent,
      isLeaf: true,
      path: [],
    }

    treeNode.path = [...(parent?.path || []), treeNode.key]

    const rawChildren = children(rawNode)
    if (Array.isArray(rawChildren) && rawChildren.length) {
      treeNode.isLeaf = false
      treeNode.children = createTreeNode<Raw>(
        {
          raw: rawChildren,
          disabled,
          key,
          children,
          label,
          level: level + 1,
          parent: treeNode,
          customCache,
          forEach,
        },
        cache
      )
    }
    treeNodes.push(treeNode)
    cache.key.set(treeNode.key, treeNode)
    Object.keys(customCache).forEach((key) => {
      customCache[key](treeNode, cache[key])
    })
    forEach?.(treeNode)
  })
  return treeNodes as any
}
